/****************************************************************
 * internal bitset used by the paging system to find free pages 
 * code modified from http://jamesmolloy.co.uk/tutorial_html/6.-Paging.html
 * by James Molloy 
*****************************************************************/
#include "mm.h"
#include "string.h"
#include "kernel.h"

/* this file keeps track of available physical frames, and will
 * give you the address of one when you need it*/

// Macros used in the bitset algorithms.
#define INDEX_FROM_BIT(a)  (a/(8*4))
#define OFFSET_FROM_BIT(a) (a%(8*4))

// A bitset of frames - used or free.
static u32 frames[INDEX_FROM_BIT(TOTAL_RAM / 0x1000)];
static u32 nframes = TOTAL_RAM / 0x1000;

static u32 first_frame();
static void set_frame(u32);
static u32 test_frame(u32);


void init_phys_mm() {
  int i;
  memset(frames, 0, INDEX_FROM_BIT(nframes));

  // mark kernel memory as used in frames table
  for(i = 0; i < KERNEL_END; i += 0x1000)
    set_frame(i);
}

// returns first available frame
u32 get_frame() {
  u32 frame = first_frame();
  frame *= 0x1000;
  set_frame(frame);
  return frame;
}

// freese a frame addres
void free_frame(u32 frame_addr) {
  u32 frame = frame_addr/0x1000;
  u32 idx = INDEX_FROM_BIT(frame);
  u32 off = OFFSET_FROM_BIT(frame);
  frames[idx] &= ~(0x1 << off);
}

// Static function to set a bit in the frames bitset
static void set_frame(u32 frame_addr) {
  u32 frame = frame_addr/0x1000;
  u32 idx = INDEX_FROM_BIT(frame);
  u32 off = OFFSET_FROM_BIT(frame);
  frames[idx] |= (0x1 << off);
}

// Static function to test if a bit is set.
static u32 test_frame(u32 frame_addr) {
	u32 frame = frame_addr/0x1000;
	u32 idx = INDEX_FROM_BIT(frame);
	u32 off = OFFSET_FROM_BIT(frame);
	return (frames[idx] & (0x1 << off));
}

// Static function to find the first free frame.
static u32 first_frame() {
  u32 i, j;

  for (i = 0; i < INDEX_FROM_BIT(nframes); i++) {
    // nothing free, exit early.
    if (frames[i] != 0xFFFFFFFF) {
      // at least one bit is free here.
      for (j = 0; j < 32; j++) {
	u32 toTest = 0x1 << j;
	
	if ( !(frames[i]&toTest) ) 
	  return i*4*8+j;
      }
    }
  }
  return 0;
}
